/* vim: set sw=4 sts=4 : */

/*
 * Copyright (c) 2009, David B. Cortarello & Rubén Bonardi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the <organization>.
 * 4. Neither the name of the <organization> nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>

#include "memory.h"
#include "datastruct.h"
#include "commands.h"
#include "debug.h"

/* Thread's creation callback */
void *ConnectionManager(void *parm) {
	iConnections *self = (iConnections *)parm;
	
	self->data = xmalloc(NULL, CONNECTION_BUFF_SIZE);

	if (auth(self))
		goto Ender;

	DoCommands(self);

Ender:
	close(self->conn);
	xfree(self->data);
	memset(self, '\0', sizeof(iConnections));
	self->conn = -1;
	pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
	struct sockaddr_in serv, client;
	int sock = 0, tsock = 0, on = 1, i = 0, port = 3333;
	socklen_t size_client = 0;
	iConnections Conn[MAX_CONNECTIONS];

	/* Dumb command line parser */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d"))
			DEBUG_LEVEL = 5;
		if (!strcmp(argv[i], "-n")) {
			if ((i = fork()) < 0) {
				derror("%s\n", strerror(errno));
			}
			else if (i > 0) {
				exit(0);
			}
		}
		if (!strcmp(argv[i], "-p")) {
			if (argv[++i] != NULL) {
				if ((port = atoi(argv[i])) <= 0) {
					fprintf(stderr, "Bad port number given\n");
					exit(1);
				}
			}
			else {
				fprintf(stderr, "Bad port number given\n");
				exit(1);
			}
		}
	}

	/* Initialize connection's array */
	for (i = 0; i < MAX_CONNECTIONS; i++)
		Conn[i].conn = -1;

	/* Create the socket */
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		derror("%s\n", strerror(errno));

	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
		derror("%s\n", strerror(errno));

	/* Bind the socket with the server structure */
	memset(&serv, '\0', sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_addr.s_addr = INADDR_ANY;
	serv.sin_port = htons(port);
	if (bind(sock, (struct sockaddr *)&serv, sizeof(struct sockaddr)) == -1)
		derror("%s\n", strerror(errno));

	/* Put the socket as passive */
	if (listen(sock, BACKLOG) == -1)
		derror("%s\n", strerror(errno));

	/* Initialize the whole thing */
	debug(DEBUG, "Waiting for connections...\n");

	while (1) {

		/* Accept a new connection */
		if ((tsock = accept(sock, (struct sockaddr *)&client, &size_client)) == -1)
			derror("%s\n", strerror(errno));

		/* Check if there's free space in the connection's array */
		for (i = 0; i < MAX_CONNECTIONS && Conn[i].conn != -1; i++)
			;
		if (i == MAX_CONNECTIONS-1) {
			write(tsock, NO_MORE_CONNECTIONS, strlen(NO_MORE_CONNECTIONS));
			close(tsock);
		}
		else {
			/* Store the new connection in the connection's array */
			Conn[i].conn = tsock;
			strncpy(Conn[i].address, inet_ntoa(client.sin_addr), 15);

			/* Manage the connection */
			if (pthread_create(&Conn[i].mt, NULL, ConnectionManager, (void *)(Conn+i)) != 0)
				derror("%s\n", strerror(errno));
		}
	}

	/* Close the listener socket */
	close(sock);
	return 0;
}

/* EOF */


